package org.reborn.utils.niobuffer

import scala.math
import java.nio.{Buffer,ByteBuffer,ByteOrder,FloatBuffer,IntBuffer,CharBuffer,ShortBuffer,LongBuffer,DoubleBuffer}

object BufferUtil {

  def resizeBuffer(buffer:FloatBuffer,bufferLength:Int,additionalSize:Int):FloatBuffer = {
    val remainingAmount = buffer.capacity - (additionalSize + bufferLength)
    if(remainingAmount >= 0)
      return buffer
    
    val extendAmount = math.max(-remainingAmount,additionalSize + remainingAmount)
    
    val newBuffer:FloatBuffer = createDirectFloatBuffer(buffer.capacity + extendAmount)
    buffer.rewind
    newBuffer.put(buffer)
    newBuffer
  }
  
  def resizeBuffer(buffer:IntBuffer,bufferLength:Int,additionalSize:Int):IntBuffer = {
    val remainingAmount = buffer.capacity - (additionalSize + bufferLength)
    if(remainingAmount >= 0)
      return buffer
    
    val extendAmount = math.max(-remainingAmount,additionalSize + remainingAmount)
    
    val newBuffer:IntBuffer = createDirectIntBuffer(buffer.capacity + extendAmount)
    buffer.rewind
    newBuffer.put(buffer)
    newBuffer
  }
  
  /**
   * Create a direct, native-order, ByteBuffer of the specified size.
   * @param size the number of bytes the buffer will hold
   * @return The newly-allocated ByteBuffer
   */  
  def createDirectByteBuffer(size:Int):ByteBuffer = {
    try{
    	ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder)
    }
    catch{
      case e:OutOfMemoryError => {
        //This is not guaranteed to prevent another OutOfMemoryError, but there is a chance that
        //this exception can be avoided by forcing garbage collection
        System.gc()
        ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder)
      }
        
    }
  }

  /**
   * Create a direct, native-order, CharBuffer of the specified size.
   * Backing ByteBuffer will have length 2*size
   * @param size the number of chars the buffer will hold
   * @return The newly-allocated CharBuffer
   */
  def createDirectCharBuffer(size:Int):CharBuffer = {
	createDirectByteBuffer(size * 2).asCharBuffer
  }
  
  /**
   * Create a direct, native-order, ShortBuffer of the specified size.
   * Backing ByteBuffer will have length 2*size
   * @param size the number of shorts the buffer will hold
   * @return The newly-allocated ShortBuffer
   */
  def createDirectShortBuffer(size:Int):ShortBuffer = {
	createDirectByteBuffer(size * 2).asShortBuffer
  }
  
  /**
   * Create a direct, native-order, IntBuffer of the specified size.
   * Backing ByteBuffer will have length 4*size
   * @param size the number of ints the buffer will hold
   * @return The newly-allocated IntBuffer
   */
  def createDirectIntBuffer(size:Int):IntBuffer = {
    createDirectByteBuffer(size * 4).asIntBuffer
  }

  /**
   * Create a direct, native-order, FloatBuffer of the specified size.
   * Backing ByteBuffer will have length 4*size
   * @param size the number of floats the buffer will hold
   * @return The newly-allocated FloatBuffer
   */
  def createDirectFloatBuffer(size:Int):FloatBuffer = {
	createDirectByteBuffer(size * 4).asFloatBuffer
  }
  
  /**
   * Create a direct, native-order, LongBuffer of the specified size.
   * Backing ByteBuffer will have length 8*size
   * @param size the number of longs the buffer will hold
   * @return The newly-allocated LongBuffer
   */
  def createDirectLongBuffer(size:Int):LongBuffer = {
	createDirectByteBuffer(size * 8).asLongBuffer
  }
  
  /**
   * Create a direct, native-order, DoubleBuffer of the specified size.
   * Backing DoubleBuffer will have length 8*size
   * @param size the number of doubles the buffer will hold
   * @return The newly-allocated DoubleBuffer
   */
  def createDirectDoubleBuffer(size:Int):DoubleBuffer = {
	createDirectByteBuffer(size * 8).asDoubleBuffer
  }

}

